interface RequestOption {
  isToken?: boolean;
  token?: string;
}

export class Request {
  private baseUrl: string
  private isToken: boolean
  private token: string | null

  constructor(baseUrl = '', option?: RequestOption) {
    this.baseUrl = baseUrl
    this.isToken = option?.isToken ?? true
    this.token = option?.token ?? localStorage.getItem('token') ?? null
  }

  private updateTokenFromStorage() {
    this.token = localStorage.getItem('token') ?? null
  }

  private buildHeaders(): Record<string, string> {
    if (this.token == null) this.updateTokenFromStorage()

    const headers: Record<string, string> = {
      'Content-Type': 'application/json'
    }

    if (this.isToken && this.token) {
      headers['X-Auth-Token'] = this.token
    }

    return headers
  }

  private async _request<T>(method: string, url: string, data?: string | Object | ArrayBuffer): Promise<T> {
    const headers = this.buildHeaders()
    const config: RequestInit = { method, headers }

    if (data && ['POST', 'PUT', 'PATCH'].includes(method)) {
      config.body = data instanceof ArrayBuffer
        ? data
        : typeof data === 'object'
          ? JSON.stringify(data)
          : String(data)
    }

    const response = await fetch(this.baseUrl + url, config)

    if (!response.ok) {
      // this.handleError(response)
      throw new Error(`HTTP ${response.status}: ${response.statusText}`)
    }

    if (!this.isToken) {
      const token = response.headers.get('x-subject-token')
      if (token) {
        localStorage.setItem('token', token)
        this.token = token
      }
    }

    return await response.json() as T
  }

  get<T>(url: string): Promise<T> {
    return this._request<T>('GET', url)
  }

  post<T>(url: string, data?: string | Object | ArrayBuffer): Promise<T> {
    return this._request<T>('POST', url, data)
  }

  put<T>(url: string, data?: string | Object | ArrayBuffer): Promise<T> {
    return this._request<T>('PUT', url, data)
  }

  delete<T>(url: string): Promise<T> {
    return this._request<T>('DELETE', url)
  }
}
